今天我們繼續談 strategy 模式。
DAY10 提到的案例,我們今天來用程式碼來簡單實作一下:
首先,先(使用 Java)實作 TaskController & SalesOrder。
class TaskController {
    public void proccess() {
        // Controller 的一些操作
        // 取得所在國家資訊等等
    		CalcTax myTax;
    		myTax = getTaxRuleForCountry();
    		SalesOrder mySO = new SalesOrder();
    		mySO.process(myTax);
  	}
  	public CalcTax getTaxRuleForCountry() {
      	// 開發時,需要實際取得所在國的稅務規則
      	// 也可以放在 config 檔中
      	// 這裡先回傳 AmericaTax
      	return new AmericaTax();
    }
  
  	// SalesOrder 類別
  	public class SalesOrder {
    		public void process(CalcTax taxToUse) {
          	int itemNum = 0;
          	int price = 0;
          	// 商品價格的商務邏輯運算
          	// ...
          	// 計算稅額
          	double tax = taxToUse.taxAmount(itemNum, price);
          	
        }
  	}
}
再來,我們來看 CalcTax 與其衍生的不同國家的計算稅額類別:
// CalcTax 抽象類別
abstract class CalcTax {
    abstract public double taxAmount (
        int itemNum,
        int price
    );
}
class AmericaTax {
  	public double taxAmount (
    		int itemNum,
        int price
    ) {
      	// 實作美國稅額計算
      	// 先 return 0
      	return 0.0;
    }
}
class FranceTax {
  	public double taxAmount (
    		int itemNum,
        int price
    ) {
      	// 實作法國稅額計算
      	// 先 return 0
      	return 0.0;
    }
}
這樣就算實作完我們的這個案例了!(當然內部的註解部分需要填充商務邏輯的細節進去)
首先,第一個優點是提高內聚;第二,提高程式碼的靈活度,因為當有新的國家需要新增相對的稅額計算,我們只需要再從 CalcTax 衍生出新類別實作即可。
第三,這讓職責的轉移變得更加容易。若是使用「類別再利用」的繼承方式,我們就必須在 TaskController 決定我們該使用哪一個 SalesOrder 物件;使用上述的方法,我們的 SalesOrder 維持不變,而其中要使用到哪種 CalcTax 的衍生類別,可以由 TaskController 或 SalesOrder 決定,其中後者也許需要有 config 檔協助確定。
第四,從 SalesOrder 來看,它用了聚合代替繼承。被包含的類別(CalcTax 的衍生類別們)如何處理變化,SalesOrder 並不關心。
GoF 如是說:
(Strategy 模式)定義一系列的演算法,把它們一個個封裝起來,並且使它們可以互相抽換。Strategy 模式使演算法可獨立於使用它的客戶而變化。
此模式建立於幾項原則之上。
Strategy 模式的關鍵特徵如下表
| 項目 | 描述 | 
|---|---|
| 意圖 | 可以根據上下文使用不同的商務邏輯或演算法 | 
| 問題 | 對所需演算法的選擇取決於發出請求的客戶或者需要處理的資料而定 | 
| 解決方案 | 把演算法的選擇與演算法的實作分離開來。允許根據上下文進行選擇 | 
| 參與者與協作者 | 1. Strategy 指定了如何使用不同的演算法2. 各 ConcreteStrategy 實作不同的演算法3. 上下文透過類型為 Strategy 的參照使用具體的 ConcreteStrategy | 
| 效果 | 1. 定義了一系列的演算法2. 可以不用 switch-case3. 要使用哪種 ConcreteStrategy 取決於上下文給予的狀態資訊 | 
| 實作 | 讓使用演算法的類別 (Context) 包含一個抽象類別 (Strategy)。該抽象類別有個 abstract method 指定演算法,每個衍生的類別依各自需求來實作演算法 | 
以下為 UML 圖。

明天我們會再繼續介紹下一個設計模式:Bridge 模式。
See ya!